home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / pascal / tpasync.zip / ASYNC4U.PAS next >
Pascal/Delphi Source File  |  1990-02-08  |  17KB  |  440 lines

  1. {----------------------------------------------------------------------}
  2. {                          ASYNC4U.PAS                                 }
  3. {                                                                      }
  4. {  This is a faithful translation of the famous ASYNC.INC by Michael   }
  5. {  Quinlan into a Turbo 4.0 unit.  No extra frills, no modification of }
  6. {  types, nothing fancy.  But with this code you should be able to     }
  7. {  delete your $I ASYNC.INC directive, add a USES ASYNC4U statement,   }
  8. {  and recompile your existing program.  If you want to add support    }
  9. {  for more ports, other computers, or change to use the new data      }
  10. {  types, all good ideas, go right ahead. With this you don't have to. }
  11. {                                                                      }
  12. {                                      Scott Gurvey, November 29 1987  }
  13. {----------------------------------------------------------------------}
  14. {                                                                      }
  15. {                          ASYNC.INC                                   }
  16. {                                                                      }
  17. {  Async Communication Routines                                        }
  18. {  by Michael Quinlan                                                  }
  19. {  with a bug fixed by Scott Herr                                      }
  20. {  made PCjr-compatible by W. M. Miller                                }
  21. {  Highly dependant on the IBM PC and PC DOS 2.0                       }
  22. {                                                                      }
  23. {  based on the DUMBTERM program by CJ Dunford in the January 1984     }
  24. {  issue of PC Tech Journal.                                           }
  25. {                                                                      }
  26. {  Entry points:                                                       }
  27. {                                                                      }
  28. {    Async_Init                                                        }
  29. {      Performs initialization.                                        }
  30. {                                                                      }
  31. {    Async_Open(Port, Baud : Integer;                                  }
  32. {               Parity : Char;                                         }
  33. {               WordSize, StpBits : Integer) : Boolean                 }
  34. {      Sets up interrupt vector, initialies the COM port for           }
  35. {      processing, sets pointers to the buffer.  Returns FALSE if COM  }
  36. {      port not installed.                                             }
  37. {                                                                      }
  38. {    Async_Buffer_Check(var C : Char) : Boolean                        }
  39. {      If a character is available, returns TRUE and moves the         }
  40. {        character from the buffer to the parameter                    }
  41. {      Otherwise, returns FALSE                                        }
  42. {                                                                      }
  43. {    Async_Send(C : Char)                                              }
  44. {      Transmits the character.                                        }
  45. {                                                                      }
  46. {    Async_Send_String(S : LStr)                                       }
  47. {      Calls Async_Send to send each character of S.                   }
  48. {                                                                      }
  49. {    Async_Close                                                       }
  50. {      Turn off the COM port interrupts.                               }
  51. {      **MUST** BE CALLED BEFORE EXITING YOUR PROGRAM; otherwise you   }
  52. {      will see some really strange errors and have to re-boot.        }
  53. {                                                                      }
  54. {----------------------------------------------------------------------}
  55.  
  56. {$B-} { Short circuit boolean ON }
  57. {$I-} { I/O hecking OFF }
  58. {$R-} { Range checking OFF }
  59. {$S-} { Stack checking OFF }
  60. {$V-} { Var-str checking OFF}
  61.  
  62. UNIT ASYNC4U;
  63.  
  64. INTERFACE
  65.  
  66. USES Dos, crt,
  67.   standard;
  68.  
  69. { global declarations }
  70.  
  71. type
  72.   LStr = String[255];  { generic string type for parameters }
  73.  
  74. const
  75.   Async_Buffer_Max = 4095;
  76.  
  77. var
  78.   Async_OriginalVector : pointer;
  79.   Async_Buffer       : Array[0..Async_Buffer_Max] of char;
  80.  
  81.   Async_Open_Flag    : Boolean;   { true if Open but no Close }
  82.   Async_Port         : Integer;   { current Open port number (1 or 2) }
  83.   Async_Base         : Integer;   { base for current open port }
  84.   Async_Irq          : Integer;   { irq for current open port }
  85.  
  86.   Async_Buffer_Overflow : Boolean;  { True if buffer overflow has happened }
  87.   Async_Buffer_Used     : Integer;
  88.   Async_MaxBufferUsed   : Integer;
  89.  
  90.     { Async_Buffer is empty if Head = Tail }
  91.   Async_Buffer_Head  : Integer;   { Locn in Async_Buffer to put next char }
  92.   Async_Buffer_Tail  : Integer;   { Locn in Async_Buffer to get next char }
  93.   Async_Buffer_NewTail : Integer;
  94.  
  95. {----------------------------------------------------------------------------}
  96. {                          USER CALLABLE ROUTINES                            }
  97. {----------------------------------------------------------------------------}
  98.  
  99. procedure Async_Init;
  100. { initialize variables }
  101.  
  102. procedure Async_Close;
  103. { reset the interrupt system when UART interrupts no longer needed }
  104.  
  105. function Async_Open(ComPort       : Integer;
  106.                     BaudRate      : Integer;
  107.                     Parity        : Char;
  108.                     WordSize      : Integer;
  109.                     StopBits      : Integer) : Boolean;
  110. { open a communications port }
  111.  
  112. function Async_Buffer_Check(var C : Char) : Boolean;
  113. { see if a character has been received; return it if yes }
  114.  
  115. procedure Async_Send(C : Char);
  116. { transmit a character }
  117.  
  118. procedure Async_Send_String(S : LStr);
  119. { transmit a string }
  120.  
  121. {----------------------------------------------------------------------------}
  122.  
  123. IMPLEMENTATION
  124.  
  125.  
  126. const
  127.   UART_THR = $00;    { offset from base of UART Registers for IBM PC }
  128.   UART_RBR = $00;
  129.   UART_IER = $01;
  130.   UART_IIR = $02;
  131.   UART_LCR = $03;
  132.   UART_MCR = $04;
  133.   UART_LSR = $05;
  134.   UART_MSR = $06;
  135.  
  136.   I8088_IMR = $21;   { port address of the Interrupt Mask Register }
  137.  
  138.  
  139. var
  140.  
  141.   Async_BIOS_Port_Table : Array[1..2] of Integer absolute $40:0;
  142.                { This table is initialized by BIOS equipment determination
  143.                  code at boot time to contain the base addresses for the
  144.                  installed async adapters.  A value of 0 means "not in-
  145.                  stalled." }
  146.  
  147. const
  148.   Async_Num_Bauds = 8;
  149.   Async_Baud_Table : array [1..Async_Num_Bauds] of record
  150.                                                      Baud, Bits : integer
  151.                                                    end
  152.                    = ((Baud:110;  Bits:$00),
  153.                       (Baud:150;  Bits:$20),
  154.                       (Baud:300;  Bits:$40),
  155.                       (Baud:600;  Bits:$60),
  156.                       (Baud:1200; Bits:$80),
  157.                       (Baud:2400; Bits:$A0),
  158.                       (Baud:4800; Bits:$C0),
  159.                       (Baud:9600; Bits:$E0));
  160.  
  161.  
  162. PROCEDURE DisableInterrupts; inline($FA {cli} );     {MACROS}
  163. PROCEDURE EnableInterrupts;  inline($FB {sti} );
  164.  
  165.  
  166. procedure BIOS_RS232_Init(ComPort, ComParm : Integer);
  167. { Issue Interrupt $14 to initialize the UART }
  168. { See the IBM PC Technical Reference Manual for the format of ComParm }
  169. var
  170.   Regs : registers;
  171. begin
  172.   with Regs do
  173.     begin
  174.       ax := ComParm and $00FF;  { AH=0; AL=ComParm }
  175.       dx := ComPort;
  176.       Intr($14, Regs)
  177.     end
  178. end; { BIOS_RS232_Init }
  179.  
  180.  
  181. {----------------------------------------------------------------------}
  182. {                                                                      }
  183. {  ISR - Interrupt Service Routine                                     }
  184. {                                                                      }
  185. {----------------------------------------------------------------------}
  186.  
  187. procedure Async_Isr;  INTERRUPT;
  188. { Interrupt Service Routine }
  189. { Invoked when the UART has received a byte of data from the
  190.   communication line }
  191.  
  192. { re-written 9/10/84 to be entirely in machine language; original source
  193.   left as comments }
  194.  
  195. begin
  196.  
  197.   Inline(
  198.     $FB/                           { STI }
  199.       { get the incomming character }
  200.       { Async_Buffer[Async_Buffer_Head] := Chr(Port[UART_RBR + Async_Base]); }
  201.     $8B/$16/Async_Base/            { MOV DX,Async_Base }
  202.     $EC/                           { IN AL,DX }
  203.     $8B/$1E/Async_Buffer_Head/     { MOV BX,Async_Buffer_Head }
  204.     $88/$87/Async_Buffer/          { MOV Async_Buffer[BX],AL }
  205.       { Async_Buffer_NewHead := Async_Buffer_Head + 1; }
  206.     $43/                           { INC BX }
  207.       { if Async_Buffer_NewHead > Async_Buffer_Max then
  208.           Async_Buffer_NewHead := 0; }
  209.     $81/$FB/Async_Buffer_Max/      { CMP BX,Async_Buffer_Max }
  210.     $7E/$02/                       { JLE L001 }
  211.     $33/$DB/                       { XOR BX,BX }
  212.       { if Async_Buffer_NewHead = Async_Buffer_Tail then
  213.           Async_Buffer_Overflow := TRUE
  214.         else }
  215. {L001:}
  216.     $3B/$1E/Async_Buffer_Tail/     { CMP BX,Async_Buffer_Tail }
  217.     $75/$08/                       { JNE L002 }
  218.     $C6/$06/Async_Buffer_Overflow/$01/ { MOV Async_Buffer_Overflow,1 }
  219.     $90/                           { NOP generated by assembler for some reason }
  220.     $EB/$16/                       { JMP SHORT L003 }
  221.       { begin
  222.           Async_Buffer_Head := Async_Buffer_NewHead;
  223.           Async_Buffer_Used := Async_Buffer_Used + 1;
  224.           if Async_Buffer_Used > Async_MaxBufferUsed then
  225.             Async_MaxBufferUsed := Async_Buffer_Used
  226.         end; }
  227. {L002:}
  228.     $89/$1E/Async_Buffer_Head/     { MOV Async_Buffer_Head,BX }
  229.     $FF/$06/Async_Buffer_Used/     { INC Async_Buffer_Used }
  230.     $8B/$1E/Async_Buffer_Used/     { MOV BX,Async_Buffer_Used }
  231.     $3B/$1E/Async_MaxBufferUsed/   { CMP BX,Async_MaxBufferUsed }
  232.     $7E/$04/                       { JLE L003 }
  233.     $89/$1E/Async_MaxBufferUsed/   { MOV Async_MaxBufferUsed,BX }
  234. {L003:}
  235.       { disable interrupts }
  236.     $FA/                           { CLI }
  237.       { Port[$20] := $20; }  { use non-specific EOI }
  238.     $B0/$20/                       { MOV AL,20h }
  239.     $E6/$20                        { OUT 20h,AL }
  240.        )
  241. end; { Async_Isr }
  242.  
  243. procedure Async_Init;
  244. { initialize variables }
  245. begin
  246.   Async_Open_Flag := FALSE;
  247.   Async_Buffer_Overflow := FALSE;
  248.   Async_Buffer_Used := 0;
  249.   Async_MaxBufferUsed := 0;
  250. end; { Async_Init }
  251.  
  252. procedure Async_Close;
  253. { reset the interrupt system when UART interrupts no longer needed }
  254. var
  255.   i, m : Integer;
  256. begin
  257.   if Async_Open_Flag then
  258.     begin
  259.  
  260.       { disable the IRQ on the 8259 }
  261.       DisableInterrupts;
  262.       i := Port[I8088_IMR];        { get the interrupt mask register }
  263.       m := 1 shl Async_Irq;        { set mask to turn off interrupt }
  264.       Port[I8088_IMR] := i or m;
  265.  
  266.       { disable the 8250 data ready interrupt }
  267.       Port[UART_IER + Async_Base] := 0;
  268.  
  269.       { disable OUT2 on the 8250 }
  270.       Port[UART_MCR + Async_Base] := 0;
  271.       EnableInterrupts;
  272.  
  273.       SetIntVec(Async_Irq + 8,Async_OriginalVector);
  274.  
  275.       { re-initialize our data areas so we know the port is closed }
  276.       Async_Open_Flag := FALSE
  277.  
  278.     end
  279. end; { Async_Close }
  280.  
  281. function Async_Open(ComPort       : Integer;
  282.                     BaudRate      : Integer;
  283.                     Parity        : Char;
  284.                     WordSize      : Integer;
  285.                     StopBits      : Integer) : Boolean;
  286. { open a communications port }
  287. var
  288.   ComParm : Integer;
  289.   i, m : Integer;
  290. begin
  291.   if Async_Open_Flag then Async_Close;
  292.  
  293.   if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
  294.     Async_Port := 2
  295.   else
  296.     Async_Port := 1;  { default to COM1 }
  297.   Async_Base := Async_BIOS_Port_Table[Async_Port];
  298.   Async_Irq := Hi(Async_Base) + 1;
  299.  
  300.   if (Port[UART_IIR + Async_Base] and $00F8) <> 0 then
  301.     Async_Open := FALSE
  302.   else
  303.     begin
  304.       Async_Buffer_Head := 0;
  305.       Async_Buffer_Tail := 0;
  306.       Async_Buffer_Overflow := FALSE;
  307.  
  308.   { Build the ComParm for RS232_Init }
  309.   { See Technical Reference Manual for description }
  310.  
  311.       ComParm := $0000;
  312.  
  313.   { Set up the bits for the baud rate }
  314.       i := 0;
  315.       repeat
  316.         i := i + 1
  317.       until (Async_Baud_Table[i].Baud = BaudRate) or (i = Async_Num_Bauds);
  318.       ComParm := ComParm or Async_Baud_Table[i].Bits;
  319.  
  320.       if Parity in ['E', 'e'] then ComParm := ComParm or $0018
  321.       else if Parity in ['O', 'o'] then ComParm := ComParm or $0008
  322.       else ComParm := ComParm or $0000;  { default to No parity }
  323.  
  324.       if WordSize = 7 then ComParm := ComParm or $0002
  325.       else ComParm := ComParm or $0003;  { default to 8 data bits }
  326.  
  327.       if StopBits = 2 then ComParm := ComParm or $0004
  328.       else ComParm := ComParm or $0000;  { default to 1 stop bit }
  329.  
  330.   { use the BIOS COM port initialization routine to save typing the code }
  331.       BIOS_RS232_Init(Async_Port - 1, ComParm);
  332.  
  333.       GetIntVec(Async_Irq + 8, Async_OriginalVector);
  334.       SetIntVec(Async_Irq + 8, @Async_Isr);
  335.  
  336.   { read the RBR and reset any possible pending error conditions }
  337.   { first turn off the Divisor Access Latch Bit to allow access to RBR, etc. }
  338.  
  339.       DisableInterrupts;
  340.  
  341.       Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] and $7F;
  342.   { read the Line Status Register to reset any errors it indicates }
  343.       i := Port[UART_LSR + Async_Base];
  344.   { read the Receiver Buffer Register in case it contains a character }
  345.       i := Port[UART_RBR + Async_Base];
  346.  
  347.   { enable the irq on the 8259 controller }
  348.       i := Port[I8088_IMR];  { get the interrupt mask register }
  349.       m := (1 shl Async_Irq) xor $00FF;
  350.       Port[I8088_IMR] := i and m;
  351.  
  352.   { enable the data ready interrupt on the 8250 }
  353.       Port[UART_IER + Async_Base] := $01; { enable data ready interrupt }
  354.  
  355.   { enable OUT2 on 8250 }
  356.       i := Port[UART_MCR + Async_Base];
  357.       Port[UART_MCR + Async_Base] := i or $08;
  358.  
  359.       EnableInterrupts;
  360.       Async_Open_Flag := TRUE;  { bug fix by Scott Herr }
  361.       Async_Open := TRUE
  362.     end
  363. end; { Async_Open }
  364.  
  365. function Async_Buffer_Check(var C : Char) : Boolean;
  366. { see if a character has been received; return it if yes }
  367. begin
  368.   if Async_Buffer_Head = Async_Buffer_Tail then
  369.     Async_Buffer_Check := FALSE
  370.   else
  371.     begin
  372.       C := Async_Buffer[Async_Buffer_Tail];
  373.       Async_Buffer_Tail := Async_Buffer_Tail + 1;
  374.       if Async_Buffer_Tail > Async_Buffer_Max then
  375.         Async_Buffer_Tail := 0;
  376.       Async_Buffer_Used := Async_Buffer_Used - 1;
  377.       Async_Buffer_Check := TRUE
  378.     end
  379. end; { Async_Buffer_Check }
  380.  
  381. procedure Async_Send(C : Char);
  382. { transmit a character }
  383. var
  384.   ch            : char;
  385.   i, m, counter : Integer;
  386. begin
  387.   Port[UART_MCR + Async_Base] := $0B; { turn on OUT2, DTR, and RTS }
  388.  
  389.   { wait for CTS }
  390.   counter := MaxInt;
  391.   while (counter <> 0) and ((Port[UART_MSR + Async_Base] and $10) = 0) do
  392.     counter := counter - 1;
  393.  
  394.   { wait for Transmit Hold Register Empty (THRE) }
  395.   if counter <> 0 then counter := MaxInt;
  396.   while (counter <> 0) and ((Port[UART_LSR + Async_Base] and $20) = 0) do
  397.     counter := counter - 1;
  398.  
  399.   if counter <> 0 then
  400.     begin
  401.       { send the character }
  402.       DisableInterrupts;
  403.       Port[UART_THR + Async_Base] := Ord(C);
  404.       EnableInterrupts
  405.     end
  406.   else
  407.     begin
  408.       save_screen (screen);
  409.       clrscr;
  410.       highvideo;
  411.       center (05, 'Attention !!');
  412.       lowvideo;
  413.       center (07, 'Your modem is not ready.  Please make it ready and strike');
  414.       center (08, 'any key.  Strike <ESC> to abort this program.');
  415.       warning;
  416.       ch := readkey;
  417.       clrscr;
  418.       if (ch = #27) then
  419.         begin
  420.           clrscr;
  421.           highvideo;
  422.           writeln ('Program terminated.');
  423.           writeln;
  424.           writeln;
  425.           halt;
  426.         end;  { if }
  427.       restore_screen (screen);
  428.     end;  { else }
  429.  
  430. end; { Async_Send }
  431.  
  432. procedure Async_Send_String(S : LStr);
  433. { transmit a string }
  434. var
  435.   i : Integer;
  436. begin
  437.   for i := 1 to length(S) do
  438.     Async_Send(S[i])
  439. end; { Async_Send_String }
  440. end. { ASYNC4U UNIT }